/*
 * main.cpp
 *
 *  Created on: 27.04.2013
 *      Author: SomeChineseGuy
 */

#include <iostream>
#include <fstream>
#include <vector>
#include <math.h>

#include <GL3/gl3w.h>
#define GL_GLEXT_PROTOTYPES
#define GLFW_INCLUDE_GL3
#include <GL/glfw.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "shaders.h"
#include "meshes.h"

using namespace std;

vector<float> vertices;
vector<float> colors;
vector<float> uvs;

GLuint vertexBuffer;
GLuint colorBuffer;
GLuint uvBuffer;
GLuint vao;

GLuint shaderProgram;
GLuint offsetLocation, frustumScale, zNear, zFar;

GLuint positionLoc;
GLuint colorLoc;
GLuint uvLoc;
GLuint mvpMatrixLoc;
GLuint textureLoc;

GLuint textureID;

glm::mat4 MVPMatrix;
glm::mat4 MVPMatrix2;

void drawObjectsInit() {

	glm::mat4 modelMatrix;
	glm::mat4 modelMatrix2;
	glm::mat4 viewMatrix;
	glm::mat4 projectionMatrix;

	projectionMatrix = glm::perspective(60.0f, 4.0f / 3.0f, 0.1f, 100.0f);
	//viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f));
	viewMatrix = glm::lookAt(glm::vec3(2.0f, 1.0f, 1.0f),
			glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 1.0f));
	modelMatrix = glm::mat4(1.0f);
	modelMatrix = glm::translate(modelMatrix, glm::vec3(0.0f, 0.0f, 0.0f));
	//modelMatrix = glm::rotate(modelMatrix, 0.0f, glm::vec3(0.0f, 1.0f, 1.0f));
	modelMatrix = glm::scale(modelMatrix, glm::vec3(0.1f));
	modelMatrix2 = glm::mat4(1.0f);
	modelMatrix2 = glm::translate(modelMatrix2, glm::vec3(0.0f, 0.5f, 0.5f));
	modelMatrix2 = glm::rotate(modelMatrix2, 45.0f, glm::vec3(0.0f, 1.0f, 1.0f));
	modelMatrix2 = glm::scale(modelMatrix2, glm::vec3(0.1f));
	MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
	MVPMatrix2 = projectionMatrix * viewMatrix * modelMatrix2;

	glGenVertexArrays(1, &vao);

	vector<GLuint> shaders;

	string vertexShaderFile = "data/shaders/perspective-uv.vert";
	string fragmentShaderFile = "data/shaders/simple-texture.frag";

	shaders.push_back(
			createShader(GL_VERTEX_SHADER,
					readShadertoString(vertexShaderFile.c_str()),
					vertexShaderFile));
	shaders.push_back(
			createShader(GL_FRAGMENT_SHADER,
					readShadertoString(fragmentShaderFile.c_str()),
					fragmentShaderFile));

	shaderProgram = createShaderProgram(shaders);

	positionLoc = glGetAttribLocation(shaderProgram, "position");
	//colorLoc = glGetAttribLocation(shaderProgram, "color");
	uvLoc = glGetAttribLocation(shaderProgram, "uv");
	mvpMatrixLoc = glGetUniformLocation(shaderProgram, "MVPMatrix");
	textureLoc = glGetUniformLocation(shaderProgram, "testureSampler");

	vertices = readMeshToVector("data/mesh/cube/cube-vertices.vrt");
	//colors = readMeshToVector("data/mesh/cube/cube-colors.vrt");
	uvs = readMeshToVector("data/mesh/cube/cube-uv.vrt");
	for (unsigned short i = 1; i < uvs.size(); i += 2) {
		uvs[i] = 1.0f - uvs[i];
	}

	glGenBuffers(1, &vertexBuffer);
	glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
	glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float),
			&vertices[0], GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	//glGenBuffers(1, &colorBuffer);
	//glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
	//glBufferData(GL_ARRAY_BUFFER, colors.size() * sizeof(float), &colors[0],
	//		GL_STATIC_DRAW);
	//glBindBuffer(GL_ARRAY_BUFFER, 0);

	glGenBuffers(1, &uvBuffer);
	glBindBuffer(GL_ARRAY_BUFFER, uvBuffer);
	glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(float), &uvs[0], GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	glBindVertexArray(vao);

	glEnableVertexAttribArray(positionLoc);
	glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
	glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);

	//glEnableVertexAttribArray(colorLoc);
	//glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
	//glVertexAttribPointer(colorLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);

	glEnableVertexAttribArray(uvLoc);
	glBindBuffer(GL_ARRAY_BUFFER, uvBuffer);
	glVertexAttribPointer(uvLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);

	glBindVertexArray(0);


	glGenTextures(1, &textureID);
	glBindTexture(GL_TEXTURE_2D, textureID);
	glfwLoadTexture2D("data/textures/uvtemplate.tga", 0);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glBindTexture(GL_TEXTURE_2D, 0);

	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);
	glFrontFace(GL_CCW);

	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);

}

void draw() {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glUseProgram(shaderProgram);
	glBindVertexArray(vao);
	glUniformMatrix4fv(mvpMatrixLoc, 1, GL_FALSE, glm::value_ptr(MVPMatrix));
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, textureID);
	glUniform1i(textureLoc, 0);
	glDrawArrays(GL_TRIANGLES, 0, vertices.size() / 3);
	glUniformMatrix4fv(mvpMatrixLoc, 1, GL_FALSE, glm::value_ptr(MVPMatrix2));
	glDrawArrays(GL_TRIANGLES, 0, vertices.size() / 3);

	glfwSwapBuffers();
}

int main(void) {

	cout << "Started" << endl;

	//Initializing GLFW framework
	GLuint glfwInitResult = glfwInit();
	if (glfwInitResult != GL_TRUE) {
		cout << "GLFW Init Error" << endl;
		glfwTerminate();
		return 0;
	}

	//Window parameters for GLFW
	glfwOpenWindowHint(GLFW_FSAA_SAMPLES, 4); // 4x antialiasing
	glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 3); // We want OpenGL 3.3
	glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 3);
	glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //We don't want the old OpenGL
	glfwOpenWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

	GLuint openResult = glfwOpenWindow(1024, 768, 0, 0, 0, 0, 32, 0,
			GLFW_WINDOW);
	glfwSetWindowTitle("nyaa");
	if (openResult == GL_FALSE) {
		cout << ": GLFW open window error" << endl;
		glfwTerminate();
		return 1;
	}

	if (gl3wInit()) {
		fprintf(stderr, "failed to initialize OpenGL\n");
		return -1;
	}
	if (!gl3wIsSupported(3, 3)) {
		fprintf(stderr, "OpenGL 3.3 not supported\n");
		return -1;
	}

	drawObjectsInit();

	int escPressed = GLFW_RELEASE;
	bool isRunning = true;
	while (isRunning) {

		escPressed = glfwGetKey(GLFW_KEY_ESC);
		isRunning = ((escPressed == GLFW_RELEASE)
				&& (glfwGetWindowParam(GLFW_OPENED)));

		draw();

	}

	cout << "Finished" << endl;

	return 0;
}
